#include <stdio.h>
#include <iostream>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <GLTools.h>
#include <GLShaderManager.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <GLMatrixStack.h>
#include <GLGeometryTransform.h>
#include <StopWatch.h>

#include <GL/glu.h>

#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
#include <iosfwd>
#include <assert.h>

using namespace std;

// 屏幕宽高宏定义
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 400

GLuint VBO;
GLuint IBO;
GLuint gWorldLocation;
// 透视变换配置参数数据结构

GLShaderManager	shaderManager;
GLFrustum viewFrustum;

GLMatrixStack       modelViewMatrix;
GLMatrixStack       projectionMatrix;
GLGeometryTransform transformPipeline;
GLFrame             viewFrame;

const char* pVSFileName = "shader.vs";
const char* pFSFileName = "shader.fs";


static void RenderSceneCB()
{
	glClear(GL_COLOR_BUFFER_BIT);

	static float Scale = 0.0f;

	Scale += 0.1f;

	modelViewMatrix.PushMatrix(viewFrame);

	/*modelViewMatrix.PushMatrix();
	modelViewMatrix.Translate(0, 0, -4);
	modelViewMatrix.PushMatrix();*/

	modelViewMatrix.Rotate(Scale, 0.0f, 1.0f, 0.0f);

	glUniformMatrix4fv(gWorldLocation, 1, GL_FALSE, transformPipeline.GetModelViewProjectionMatrix());

	glEnableVertexAttribArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);

	glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);

	glDisableVertexAttribArray(0);

	modelViewMatrix.PopMatrix();
	//modelViewMatrix.PopMatrix();

	glutSwapBuffers();
}

static void ChangeSize(int w, int h)
{
	// Prevent a divide by zero
	if (h == 0)
		h = 1;

	// Set Viewport to window dimensions
	glViewport(0, 0, w, h);

	viewFrustum.SetPerspective(30.0f, float(w) / float(h), 1.0f, 100.0f);

	projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
	transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}

static void InitializeGlutCallbacks()
{
	glutDisplayFunc(RenderSceneCB);
	glutIdleFunc(RenderSceneCB);
	glutReshapeFunc(ChangeSize);
}

static void CreateVertexBuffer()
{
	M3DVector3f Vertices[4] = {
		-1.0f, -1.0f, 0.5773f,
		0.0f, -1.0f, -1.15475f,
		0.0f, 1.0f, 0.0f
	};

	glGenBuffers(1, &VBO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
}

static void CreateIndexBuffer()
{
	unsigned int Indices[] = { 0, 3, 1,
		1, 3, 2,
		2, 3, 0,
		0, 1, 2 };

	glGenBuffers(1, &IBO);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
}

int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
	glutInitWindowPosition(100, 100);
	glutCreateWindow("Tutorial 12");

	InitializeGlutCallbacks();

	// Must be done after glut is initialized!
	GLenum res = glewInit();
	if (res != GLEW_OK) {
		fprintf(stderr, "Error: '%s'\n", glewGetErrorString(res));
		return 1;
	}

	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

	CreateVertexBuffer();
	CreateIndexBuffer();

	shaderManager.InitializeStockShaders();
	viewFrame.MoveForward(4.0f);
	GLuint shader = shaderManager.LoadShaderPairWithAttributes("shader.vs", "shader.fs", 1, GLT_ATTRIBUTE_VERTEX, "Position1");
	glUseProgram(shader);
	gWorldLocation = glGetUniformLocation(shader, "gWorld");

	glutMainLoop();

	return 0;
}